#ifndef HERM_MSG_BASE_H
#define HERM_MSG_BASE_H

#include <string>
#include <vector>
#include "herm/export_symbols.h"
#include "herm/base/utils/exception.h"
#include "herm/base/utils/types.h"

#define HERM_MSG_EXCEPTION(type, desc) \
	herm::MsgException(__FILE__, __LINE__, __FUNCTION__, type, desc)

namespace herm{

class HERM_API MsgException : public Exception
{
public:
	MsgException(const std::string& file, int line, const std::string& func, 
		int type = 0, const std::string& desc = "message error");
};

struct Data
{
	Data(){}
	Data(char* d, int l)
	{
		data = d;
		len = l;
	}
	int len;
	char* data;
};

class HERM_API Encoder
{
public:
	Encoder(char* buf, int len);
	
	template <class T>
	Encoder& operator<<(const T& val)
	{
		val << *this;
		return *this;
	}

	Encoder& operator<<(uint8_t val);
	Encoder& operator<<(uint16_t val);
	Encoder& operator<<(uint32_t val);
	Encoder& operator<<(float val);
	Encoder& operator<<(double val);
	Encoder& operator<<(const std::string& s);

	template <class T>
	Encoder& operator<<(const std::vector<T>& vec)
	{
		for (size_t i = 0; i < vec.size(); i++)
			*this << vec[i];

		return *this;
	}

	Encoder& operator<<(const Data& data);

    typedef Encoder& (*Fun)(Encoder&);
	Encoder& operator<<(Fun f);

	inline int Length() const
	{
		return m_pos;
	}

	inline void Length(int pos)
	{
		m_pos = pos;
	}

	/**
	* @brief Force to copy data to some position of m_buf.
	*       Note, this Encode don't move m_pos!
	*/
	inline void EncodeBuf(int pos, const void* buf, int len)
	{
		if (m_len - pos < len)
			throw HERM_MSG_EXCEPTION(ST_ENCODE_FAILED, "The pos is too long!");

		memcpy(m_buf + pos, buf, len);
	}
protected:
	void EncodeSize(int sizeType, int sizeVal);
private:
	inline void EncodeBuf(const void* buf, int len)
	{
		if (m_len - m_pos < len)
			throw HERM_MSG_EXCEPTION(ST_ENCODE_FAILED, "Encode buffer is too short!");

		memcpy(m_buf + m_pos, buf, len);
		m_pos += len;
	}

private:
	char* m_buf;
	int m_len;
	int m_pos;
};

HERM_API Encoder& endl(Encoder& encoder);

class HERM_API Decoder
{
public:
	Decoder(const char* buf, int len);

	template <class T>
	const Decoder& operator>>(T& val) const
	{
		val >> *this;
		return *this;
	}

	const Decoder& operator>>(uint8_t& val) const;
	const Decoder& operator>>(uint16_t& val) const;
	const Decoder& operator>>(uint32_t& val) const;
	const Decoder& operator>>(std::string& s) const;

	template <class T>
	const Decoder& operator>>(std::vector<T>& vec) const
	{
		int count =  m_curSegSize;
		for (int i = 0; i < count; i++)
		{
			T val;
			*this >> val;
			vec.push_back(val);
		}

		return *this;
	}

	const Decoder& operator>>(Data& data) const;

protected:
	void DecodeSize(int segSize) const;
private:
	inline void DecodeBuf(void* buf, int len) const
	{
		if (m_len - m_pos < len)
			throw HERM_MSG_EXCEPTION(ST_DECODE_FAILED, "Decode buffer is end!");

		memcpy(buf, m_buf + m_pos, len);
		const_cast<Decoder*>(this)->m_pos += len;
	}
private:
	const char* m_buf;
	int m_len;
	int m_pos;
	int m_curSegSize;
};
}

#endif
